﻿#ifndef SANDBOX_COMMAND
#define SANDBOX_COMMAND
#include "../basic.h"
typedef void (*func)(qword code);
/*
 * SandBox Function Frame:
 * +--------+		High Address
 * |  Base  |
 * +--------+
 * |  Data  |
 * +--------+
 * |  TSSR  |
 * +--------+
 * |  Code  |
 * +--------+		Low Address
 */

// Define Commands
// Byte Commands
enum OP_MATH
{
    ADD,
    SUB,
    MUL,
    DIV,
};
enum OP_LOGIC
{
    AND,
    OR,
    XOR
};
enum CONDITION
{
    NONE,
    EQU,
    LT,
    LE,
    GT,
    GE,
    NEQ,
    ALL
};
void nop(qword code = 0)
{
    int n = 0x1000;
    while (n)
        n--;
}
void ret(qword code = 0)
{
    // ret command:
    // return from subblock
    qword base = addr_regs[Base];
    strncpy((char *)(addr_regs + Code), (char *)(ram + base), 8);
    strncpy((char *)(special_regs + TSSR), (char *)(ram + base + 8), 8);
    strncpy((char *)(addr_regs + Data), (char *)(ram + base + 16), 8);
    strncpy((char *)(addr_regs + Base), (char *)(ram + base + 24), 8);
}
void precall(qword code = 0)
{
    // precall command
    // push basic function frame
    qword stack = addr_regs[Stack];
    strncpy((char *)(ram + stack - 8), (char *)(addr_regs + Base), 8);
    strncpy((char *)(ram + stack - 16), (char *)(addr_regs + Data), 8);
    strncpy((char *)(ram + stack - 24), (char *)(special_regs + TSSR), 8);
    strncpy((char *)(ram + stack - 32), (char *)(addr_regs + Code), 8);
    addr_regs[Stack] -= 32;
}
void pushaf(qword code = 0)
{
    // push all float(real4) regs to stack
    qword stack = addr_regs[Stack];
    for (int i = 0; i < 8; i++)
    {
        stack -= 4;
        strncpy((char *)(ram + stack), (char *)(fregs + i), 4);
    }
    addr_regs[Stack] = stack;
}
void pushad(qword code = 0)
{
    // push all float(real8) regs to stack
    qword stack = addr_regs[Stack];
    for (int i = 0; i < 8; i++)
    {
        stack -= 8;
        strncpy((char *)(ram + stack), (char *)(dregs + i), 8);
    }
    addr_regs[Stack] = stack;
}
void pushaq(qword code = 0)
{
    // push all qword Function regs to stack
    qword stack = addr_regs[Stack];
    for (int i = 0; i < 8; i++)
    {
        stack -= 8;
        strncpy((char *)(ram + stack), (char *)(func_regs + i), 8);
    }
    addr_regs[Stack] = stack;
}
void popaf(qword code = 0)
{
    // pop all float(real4) regs from stack
    qword stack = addr_regs[Stack];
    for (int i = 7; i >= 0; i--)
    {
        strncpy((char *)(fregs + i), (char *)(ram + stack), 4);
        stack += 4;
    }
    addr_regs[Stack] = stack;
}
void popad(qword code = 0)
{
    // pop all float(real8) regs from stack
    qword stack = addr_regs[Stack];
    for (int i = 7; i >= 0; i--)
    {
        strncpy((char *)(dregs + i), (char *)(ram + stack), 8);
        stack += 8;
    }
    addr_regs[Stack] = stack;
}
void popaq(qword code = 0)
{
    // pop all qword Function regs from reg
    qword stack = addr_regs[Stack];
    for (int i = 7; i >= 0; i--)
    {

        strncpy((char *)(func_regs + i), (char *)(ram + stack), 8);
        stack += 8;
    }
    addr_regs[Stack] = stack;
}

// Word Commands
void trap(qword code)
{
    // trap command
    // SandBox Trap in True Mode
    dword target = code & 0xff;
    switch (code)
    {
    case 0x00:
        break;
    case 0x01: // read keyboard
        if (!check_address(addr_regs[DI]))
        {
            // TODO:Throw Error:Address Error
        }
        for (int i = 0; i < special_regs[Count]; i++)
            *(ram + addr_regs[DI]) = getchar();
        break;
    case 0x02: // output screen
        if (!check_address(addr_regs[DI]))
        {
            // TODO:Throw Error:Address Error
        }
        for (int i = 0; i < special_regs[Count]; i++)
            putchar(*(ram + addr_regs[SI] + i));
        break;
    case 0x03: // file read
        file_rw(IN_RAM);
        break;
    case 0x04: // file write
        file_rw(OUT_RAM);
        break;
    case 0x05: // memory copy
        if ((!check_address(addr_regs[DI])) || (!check_address(addr_regs[SI])))
        {
            // TODO:Throw Error:Address Error
        }
        strncpy((char *)(ram + addr_regs[DI]), (char *)(ram + addr_regs[SI]),
                special_regs[Count]);
        break;
    case 0x10:
        mount();
        break;
    case 0x11:
        umount();
        break;
    default:
        precall(0);
        special_regs[Flag] += ENABLE;
        special_regs[Flag] += (static_cast<qword>(target) << 16);
        break;
    }
}
void setb(qword code)
{
    // setb command
    // Set Bit in special_regs[Flag]
    byte target = code & 0x3f;
    special_regs[Flag] |= ((static_cast<qword>(1)) << target);
}
void clrb(qword code)
{
    // clrb command
    // Clear Bit in special_regs[Flag]
    byte target = code & 0x3f;
    special_regs[Flag] -= ((static_cast<qword>(1)) << target);
}
void push(qword code)
{
    byte target = code & 0x1f;
    if (target >= 24)
    {
        // TODO:Throw Error:Register not found
    }
    if (target < 8)
        strncpy((char *)(ram + addr_regs[Stack]), (char *)(func_regs + target), 8);
    else if (target < 16)
        strncpy((char *)(ram + addr_regs[Stack]), (char *)(addr_regs + target - 10), 8);
    else if (target < 24)
        strncpy((char *)(ram + addr_regs[Stack]), (char *)(special_regs + target - 10), 8);
    addr_regs[Stack] -= 8;
}
void pop(qword code)
{
    byte target = code & 0x1f;
    if (target >= 24)
    {
        // TODO:Throw Error:Register not found
    }
    if (target < 8)
        strncpy((char *)(func_regs + target), (char *)(ram + addr_regs[Stack]), 8);
    else if (target < 16)
        strncpy((char *)(addr_regs + target - 10), (char *)(ram + addr_regs[Stack]), 8);
    else if (target < 24)
        strncpy((char *)(special_regs + target - 10), (char *)(ram + addr_regs[Stack]), 8);
    addr_regs[Stack] += 8;
}
void pushf(qword code)
{
    byte target = code & 0x07;
    flt_rw(IN_RAM, addr_regs[Stack], target, REAL4);
    addr_regs[Stack] -= 4;
}
void popf(qword code)
{
    byte target = code & 0x07;
    flt_rw(OUT_RAM, addr_regs[Stack], target, REAL4);
    addr_regs[Stack] += 4;
}
void pushd(qword code)
{
    byte target = code & 0x07;
    flt_rw(IN_RAM, addr_regs[Stack], target, REAL8);
    addr_regs[Stack] -= 8;
}
void popd(qword code)
{
    byte target = code & 0x07;
    flt_rw(OUT_RAM, addr_regs[Stack], target, REAL8);
    addr_regs[Stack] += 8;
}
void call(qword code)
{
    // Call Function
    // Use Page register to store the table
    byte label = code & 0xff;
    precall();
    addr_regs[Code] =
        (static_cast<qword>(*(ram + special_regs[Page] + 2 * label)) << 8) +
        *(ram + special_regs[Page] + 2 * label + 1);
}
void lsh(qword code)
{
    // lsh command
    // Left Shift the Function Register
    byte reg = code & 0x07;
    byte count = (code & 0xf8) >> 3;
    func_regs[reg] = func_regs[reg] << count;
}
void rsh(qword code)
{
    // lsh command
    // Right Shift the Function Register
    byte reg = code & 0x07;
    byte count = (code & 0xf8) >> 3;
    func_regs[reg] = func_regs[reg] >> count;
}
void inv(qword code)
{
    // inv command
    // Invert Function Register
    byte reg = code & 0x07;
    func_regs[reg] = ~func_regs[reg];
}
void movra(qword code)
{
    // movra command
    // Move Function Register to Address Register
    byte target = (code & 0x70) >> 4;
    byte source = code & 0x07;
    if (target > 5)
    {
        // TODO:Throw Error:Register Error
    }
    else
        addr_regs[target] = func_regs[source];
}
void movar(qword code)
{
    byte target = (code & 0x70) >> 4;
    byte source = code & 0x07;
    if (source > 5)
    {
        // TODO:Throw Error:Register Error
    }
    else
        func_regs[target] = addr_regs[source];
}
void movrs(qword code)
{
    byte target = (code & 0x70) >> 4;
    byte source = code & 0x07;
    if (source > 5)
    {
        // TODO:Throw Error:Register Error
    }
    else
        special_regs[target] = func_regs[source];
}
void movsr(qword code)
{
    byte target = (code & 0x70) >> 4;
    byte source = code & 0x07;
    if (target > 5)
    {
        // TODO:Throw Error:Register Error
    }
    else
        func_regs[target] = special_regs[source];
}
void movaa(qword code)
{
    byte target = (code & 0x70) >> 4;
    byte source = code & 0x07;
    if ((target > 5) || (source > 5))
    {
        // TODO:Throw Error:Register Error
    }
    else
        addr_regs[target] = addr_regs[source];
}
void movrr(qword code)
{
    byte target = (code & 0x70) >> 4;
    byte source = code & 0x07;
    func_regs[target] = func_regs[source];
}
void inc(qword code)
{
    byte reg = code & 0x07;
    ++func_regs[reg];
}
void dec(qword code)
{
    byte reg = code & 0x07;
    --func_regs[reg];
}
void cbw(qword code)
{
    byte reg = code & 0x07;
    func_regs[reg] &= 0xff;
    if (func_regs[reg] > 0x80)
        func_regs[reg] += 0xff00;
    else
        ;
}
void cwd(qword code)
{
    byte reg = code & 0x07;
    func_regs[reg] &= 0xffff;
    if (func_regs[reg] > 0x8000)
        func_regs[reg] += 0xffff0000;
    else
        ;
}
void cdq(qword code)
{
    byte reg = code & 0x07;
    func_regs[reg] &= 0xffffffff;
    if (func_regs[reg] > 0x80000000)
        func_regs[reg] += 0xffffffff00000000;
    else
        ;
}

// Dword Commands
void calr(qword code)
{
    byte opcode = (code & 0x00f00000) >> 20;
    byte regd = (code & 0x00000700) >> 8;
    byte reg1 = (code & 0x00000070) >> 4;
    byte reg2 = code & 0x07;
    switch (opcode)
    {
    case ADD:
        func_regs[regd] = func_regs[reg1] + func_regs[reg2];
        break;
    case SUB:
        func_regs[regd] = func_regs[reg1] - func_regs[reg2];
        break;
    case MUL:
        func_regs[regd] = func_regs[reg1] * func_regs[reg2];
        break;
    case DIV:
        func_regs[regd] = func_regs[reg1] / func_regs[reg2];
        break;
    }
    changeFlag(regd);
}
void logr(qword code)
{
    byte opcode = (code & 0x00f00000) >> 20;
    byte regd = (code & 0x00000700) >> 8;
    byte reg1 = (code & 0x00000070) >> 4;
    byte reg2 = code & 0x07;
    switch (opcode)
    {
    case AND:
        func_regs[regd] = func_regs[reg1] & func_regs[reg2];
        break;
    case OR:
        func_regs[regd] = func_regs[reg1] | func_regs[reg2];
        break;
    case XOR:
        func_regs[regd] = func_regs[reg1] ^ func_regs[reg2];
        break;
    }
    changeFlag(regd);
}
void calf(qword code)
{
    byte opcode = (code & 0x00f00000) >> 20;
    byte regd = (code & 0x00000700) >> 8;
    byte reg1 = (code & 0x00000070) >> 4;
    byte reg2 = code & 0x07;
    switch (opcode)
    {
    case ADD:
        fregs[regd] = fregs[reg1] + fregs[reg2];
        break;
    case SUB:
        fregs[regd] = fregs[reg1] - fregs[reg2];
        break;
    case MUL:
        fregs[regd] = fregs[reg1] * fregs[reg2];
        break;
    case DIV:
        fregs[regd] = fregs[reg1] / fregs[reg2];
        break;
    }
}
void cald(qword code)
{
    byte opcode = (code & 0x00f00000) >> 20;
    byte regd = (code & 0x00000700) >> 8;
    byte reg1 = (code & 0x00000070) >> 4;
    byte reg2 = code & 0x07;
    switch (opcode)
    {
    case ADD:
        dregs[regd] = dregs[reg1] + dregs[reg2];
        break;
    case SUB:
        dregs[regd] = dregs[reg1] - dregs[reg2];
        break;
    case MUL:
        dregs[regd] = dregs[reg1] * dregs[reg2];
        break;
    case DIV:
        dregs[regd] = dregs[reg1] / dregs[reg2];
        break;
    }
}
void ldr(qword code)
{
    byte valr = (code & 0x700000) >> 20;
    byte addr = (code & 0x70000) >> 16;
    byte type = (code & 0xf000) >> 12;
    word offset = code & 0xfff;
    ram_rw(OUT_RAM, addr_regs[addr] + offset, valr, DTYPE(type));
}
void str(qword code)
{
    byte valr = (code & 0x70000) >> 16;
    byte addr = (code & 0x700000) >> 20;
    byte type = (code & 0xf000) >> 12;
    word offset = code & 0xfff;
    ram_rw(IN_RAM, addr_regs[addr] + offset, valr, DTYPE(type));
}
void calld(qword code)
{
    dword label = code & 0xffff;
    precall();
    addr_regs[Code] = *(qword *)(ram + special_regs[Page] + label * 8);
}
void intd(qword code)
{
    qword label = code & 0xffff;
    switch (label & 0x3000)
    {
    case 0x0000:
        special_regs[Flag] += ERROR;
        special_regs[Flag] += (label << 32);
        break;
    case 0x1000:
    case 0x3000:
        special_regs[Flag] += ENABLE;
        special_regs[Flag] += ((label & 0xfff) << 16);
        break;
    }
}
// Qword Commands
void cali(qword code)
{
    byte opcode = (code & 0xf0000000000000) >> 52;
    byte regd = (code & 0x0f000000000000) >> 48;
    byte reg1 = (code & 0xf00000000000) >> 44;
    dword imm = code & 0xffffffff;
    switch (opcode)
    {
    case ADD:
        func_regs[regd] = func_regs[reg1] + imm;
        break;
    case SUB:
        func_regs[regd] = func_regs[reg1] - imm;
        break;
    case MUL:
        func_regs[regd] = func_regs[reg1] * imm;
        break;
    case DIV:
        func_regs[regd] = func_regs[reg1] / imm;
        break;
    }
}
void logi(qword code)
{
    byte opcode = (code & 0xf0000000000000) >> 52;
    byte regd = (code & 0x0f000000000000) >> 48;
    byte reg1 = (code & 0xf00000000000) >> 44;
    dword imm = code & 0xffffffff;

    switch (opcode)
    {
    case AND:
        func_regs[regd] = func_regs[reg1] & imm;
        break;
    case OR:
        func_regs[regd] = func_regs[reg1] | imm;
        break;
    case XOR:
        func_regs[regd] = func_regs[reg1] ^ imm;
        break;
    }
}
void ldi(qword code)
{
    byte valr = (code & 0xf0000000000000) >> 52;
    byte base = (code & 0x0f000000000000) >> 48;
    byte type = (code & 0x00f00000000000) >> 44;
    qword offset = code & 0x000fffffffffff;
    ram_rw(OUT_RAM, addr_regs[base] + offset, valr, DTYPE(type));
}
void sti(qword code)
{
    byte valr = (code & 0xf0000000000000) >> 52;
    byte base = (code & 0x0f000000000000) >> 48;
    byte type = (code & 0x00f00000000000) >> 44;
    qword offset = code & 0x000fffffffffff;
    ram_rw(IN_RAM, addr_regs[base] + offset, valr, DTYPE(type));
}
void jmp(qword code)
{
    qword address = code & 0xffffffffffff;
    addr_regs[Code] = address;
}
void br(qword code)
{
    byte con = (code & 0xf0000000000000) >> 52;
    byte reg = (code & 0x0f000000000000) >> 48;
    qword address = code & 0xffffffffffff;
    switch (con)
    {
    case NONE:
        break;
    case EQU:
        if (reg < 8)
        {
            if (func_regs[reg] == 0)
            {
                addr_regs[Code] &= 0xffff000000000000;
                addr_regs[Code] += address;
            }
        }
        else if (reg == 8)
        {
            if (special_regs[Count] == 0)
            {
                addr_regs[Code] &= 0xffff000000000000;
                addr_regs[Code] += address;
            }
        }
        break;
    case LT:
        if (reg < 8)
        {
            if (static_cast<long long>(func_regs[reg]) < 0)
            {
                addr_regs[Code] &= 0xffff000000000000;
                addr_regs[Code] += address;
            }
        }
        else if (reg == 8)
        {
            if (static_cast<long long>(special_regs[Count]) < 0)
            {
                addr_regs[Code] &= 0xffff000000000000;
                addr_regs[Code] += address;
            }
        }
        break;
    case LE:
        if (reg < 8)
        {
            if (static_cast<long long>(func_regs[reg]) <= 0)
            {
                addr_regs[Code] &= 0xffff000000000000;
                addr_regs[Code] += address;
            }
        }
        else if (reg == 8)
        {
            if (static_cast<long long>(special_regs[Count]) <= 0)
            {
                addr_regs[Code] &= 0xffff000000000000;
                addr_regs[Code] += address;
            }
        }
        break;
    case GT:
        if (reg < 8)
        {
            if (static_cast<long long>(func_regs[reg]) > 0)
            {
                addr_regs[Code] &= 0xffff000000000000;
                addr_regs[Code] += address;
            }
        }
        else if (reg == 8)
        {
            if (static_cast<long long>(special_regs[Count]) > 0)
            {
                addr_regs[Code] &= 0xffff000000000000;
                addr_regs[Code] += address;
            }
        }
        break;
    case GE:
        if (reg < 8)
        {
            if (static_cast<long long>(func_regs[reg]) >= 0)
            {
                addr_regs[Code] &= 0xffff000000000000;
                addr_regs[Code] += address;
            }
        }
        else if (reg == 8)
        {
            if (static_cast<long long>(special_regs[Count]) >= 0)
            {
                addr_regs[Code] &= 0xffff000000000000;
                addr_regs[Code] += address;
            }
        }
        break;
    case NEQ:
        if (reg < 8)
        {
            if (static_cast<long long>(func_regs[reg]) != 0)
            {
                addr_regs[Code] &= 0xffff000000000000;
                addr_regs[Code] += address;
            }
        }
        else if (reg == 8)
        {
            if (static_cast<long long>(special_regs[Count]) != 0)
            {
                addr_regs[Code] &= 0xffff000000000000;
                addr_regs[Code] += address;
            }
        }
        break;
    case ALL:
        jmp(code);
        break;
    }
}
// void avx(qword code);

/*
 * The first edition is unable to use avx commands.
 */
// void avx(qword code);

#endif // !SANDBOX_COMMAND
